package com.goldsprite.gameframeworks.fsm;

import com.goldsprite.gameframeworks.log.Debug;

import java.util.LinkedHashMap;
import java.util.Map;

public class Fsm {
	protected String name;
	private State currentState, defaultState;
	private final Map<Class, StateInfo> states = new LinkedHashMap<>();

	public Fsm() {
		setName(getClass().getSimpleName());
	}
	public Fsm(String name) {
		setName(name);
	}

	public boolean isState(Class<? extends State> stateClazz) {
		if(currentState == null) return false;
		return stateClazz.isAssignableFrom(currentState.getClass());
	}

	public void addState(State state, int priority) {
		state.setFsm(this);
		states.put(state.getClass(), new StateInfo(state, priority));
		if(currentState == null){
			chageState(state);
			defaultState = currentState = state;
		}
	}

	public State getState(Class key) {
		if(!states.containsKey(key)) return null;
		return states.get(key).state;
	}

	protected void chageState(State state) {
		if(currentState != null){
			Debug.logT("GFsm", "%s 退出 %s 状态", getName(), currentState.getClass().getSimpleName());
			currentState.exit();
		}
		currentState = state;
		Debug.logT("GFsm", "%s 进入 %s 状态", getName(), currentState.getClass().getSimpleName());
		currentState.enter();
	}

	public void updateFsm() {
		if (currentState != null) {
			currentState.running();

			StateInfo nextStateInfo = findNextState();

			if (nextStateInfo != null) {
				chageState(nextStateInfo.state);
			}

		}
	}

	private StateInfo findNextState() {
		StateInfo bestStateInfo = null;
		int bestPriority = -1;
		if(!currentState.canExit()) bestPriority = getCurrentStatePriority(currentState);

		for (StateInfo stateInfo : states.values()) {
			if(stateInfo.state == currentState) continue;
			if (stateInfo.priority >= bestPriority && stateInfo.state.canEnter()) {
				bestStateInfo = stateInfo;
				bestPriority = stateInfo.priority;
			}
		}

		return bestStateInfo;
	}

	private int getCurrentStatePriority(State state) {
		StateInfo stateInfo = states.values().stream().filter(i -> i.state == state).findFirst().orElse(null);
		if(stateInfo == null) return -1;
		return stateInfo.priority;
	}

	public <T> T setName(String name) {
		this.name = name;
		return (T)this;
	}
	public String getName() {
		return name;
	}

	private static class StateInfo {
		State state;
		int priority;

		StateInfo(State state, int priority) {
			this.state = state;
			this.priority = priority;
		}
	}
}
